/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include <QTextStream>

#include "DictManager.h"
#include "Options.h"
#include "Logger.h"

QString QueryResult::getFinalArticle()
{
	mFinalArticle.clear();
	QString dictTitle = QString("<FONT COLOR='BLUE'>&lt;---%1---&gt;</FONT>").arg(mDictName);
	mFinalArticle.append(dictTitle);

	if (mDictType == "xdxf")
	{
		mFinalArticle.append("<BR>");
		mFinalArticle.append(mArticle);
	}
	else if (mDictType == "txt")
	{
		mFinalArticle.append(QString("<BR><FONT COLOR='%1' SIZE=4><B>").arg("#330033"));
		mFinalArticle.append(QString("%1</B></FONT><BR>").arg(mKey));
		mFinalArticle.append(mArticle);
	}

	mFinalArticle.append("<BR>");

	return mFinalArticle;
}

DictManager::DictManager():
	xdxf_color_key("#330033"),
	xdxf_color_co("#0066ff"),
	xdxf_color_ex("#660099"),
	xdxf_color_kref("#009966"),
	xdxf_color_dtrn("#0000cc"),
	xdxf_color_tense("#3399cc")
{
	OPT.setCursorBusy(true);
	readSettings();
	loadDicts();
	OPT.setCursorBusy(false);
}

DictManager::~DictManager()
{
	writeSettings();
}

void DictManager::readSettings()
{
	QSettings settings("./configure/Dictionary.conf" , QSettings::IniFormat );
	QVariant val;
	settings.beginGroup( "XDXF" );
	val = settings.value("xdxf_color_key");
	if(val.isValid()) xdxf_color_key = val.toString();

	val = settings.value("xdxf_color_co");
	if(val.isValid()) xdxf_color_co = val.toString();

	val = settings.value("xdxf_color_ex");
	if(val.isValid()) xdxf_color_ex = val.toString();

	val = settings.value("xdxf_color_kref");
	if(val.isValid()) xdxf_color_kref = val.toString();

	val = settings.value("xdxf_color_dtrn");
	if(val.isValid()) xdxf_color_dtrn = val.toString();

	val = settings.value("xdxf_color_tense");
	if(val.isValid()) xdxf_color_tense = val.toString();

	settings.endGroup();
}

void DictManager::writeSettings()
{
	QSettings settings("./configure/Dictionary.conf" , QSettings::IniFormat );
	settings.beginGroup( "XDXF" );
	settings.setValue("xdxf_color_key", xdxf_color_key);
	settings.setValue("xdxf_color_co", xdxf_color_co);
	settings.setValue("xdxf_color_ex", xdxf_color_ex);
	settings.setValue("xdxf_color_kref", xdxf_color_kref);
	settings.setValue("xdxf_color_dtrn", xdxf_color_dtrn);
	settings.setValue("xdxf_color_tense", xdxf_color_tense);
	settings.endGroup();
}

void DictManager::loadDicts()
{
	LOG<<"Loading dictionaries..."<<endl;
	OPT.splash.showMessage("Loading dictionaries...");
	mDictDBs.clear();
	mDicts.clear();
	mDictMap.clear();

	QDir dir = QDir::current();
	dir.cd(DICT_DIR);

	QStringList filter;
	filter<<"*.db";
	QStringList files = dir.entryList(filter);
	for (int i=0; i<files.size(); i++)
	{
		QString dict = files[i];
		QString dictFile = dir.absoluteFilePath(dict);
		QString dbName = dict.mid(dict.lastIndexOf("/") + 1, dict.lastIndexOf(".db") - dict.lastIndexOf("/") - 1);
		LOG<<"dbName="<<dbName<<endl;
		mDictDBs.push_back(dbName);
		mSQLiteDB.openDB(dictFile, dbName);
	}

	QString command;
	for (int i=0; i<mDictDBs.size(); i++)
	{
		command = QString("select full_name,description,dict_type from dict_info");
		mSQLiteDB.execQuery(command, QueryT_Read, mDictDBs[i]);
		QString dictName = mSQLiteDB.getTableValue(0, "full_name");
		QString description = mSQLiteDB.getTableValue(0, "description");
		QString dictType = mSQLiteDB.getTableValue(0, "dict_type");

		mDicts.push_back(DictInfo(mDictDBs[i], dictName, dictType, description));
		mDictMap[dictName] = mDictDBs[i];
		loadKeyList(mDicts[i]);
	}
}

void DictManager::loadKeyList(DictInfo& dictInfo)
{
	QString command;

	command = "select key from articles;";
	mSQLiteDB.execQuery(command, QueryT_Read, dictInfo.mDBName);
	LOG<<"Rowcount="<<mSQLiteDB.getRowCount()<<endl;
	QString msg;
	QTextStream s(&msg);
	s<<"Loading \""<<dictInfo.mDictName<<"\"";
	OPT.splash.showMessage(msg);

	dictInfo.keys.clear();
	for (int i=0; i<mSQLiteDB.getRowCount(); ++i)
		dictInfo.keys.push_back(mSQLiteDB.getTableValue(i, 0));
}

void DictManager::setKeyMap(QStringList& queryDictList)
{
	for (int i=0; i<mDicts.size(); i++)
		if (queryDictList.contains(mDicts[i].mDictName))
			for (int j=0; j<mDicts[i].keys.size(); j++)
				mKeyMap[mDicts[i].keys[j].size()].insert(mDicts[i].keys[j]);
}

void DictManager::setQueryResult(QString dictName, QString key, bool exactly)
{
	QString dictFile = mDictMap[dictName];
	mQueryResult.clear();
	QString command;
	command = QString("Select dict_type,full_name from dict_info;");
	mSQLiteDB.execQuery(command, QueryT_Read, dictFile);
	QString dict_type = mSQLiteDB.getTableValue(0,"dict_type");
	QString full_name = mSQLiteDB.getTableValue(0,"full_name");

	key.replace("\'", "\'\'");
	command = QString("Select key,article from articles where key >= '%1' and ").arg(key); //a faster query command comparing with 'like' syntax
	QChar last = key[key.size()-1];
	char last_ascii=last.toAscii();
	last_ascii++;
	key.replace(key.size()-1,1,last_ascii);
	command.append(QString("key < '%1';").arg(key));
	mSQLiteDB.execQuery(command, QueryT_Read, dictFile);
	//LOG<<"Command="<<command.toUtf8().data()<<endl;
	for (int i=0; i<mSQLiteDB.getRowCount(); i++)
	{
		QString key_i = mSQLiteDB.getTableValue(i,"key");
		if (exactly)
		{
			if (key_i.size() > key.size())
			{
				if (key_i[key.size()] != ' ' && key_i[key.size()] != '(' && key_i[key.size()] != '$')
					continue;  //do not display
				else if (key_i.size() > key.size()+1 && key_i[key.size()] == ' ')
				{
					QString extra = key_i.right(key_i.size() - key.size());
					extra = extra.simplified();
					if (!extra.isEmpty() && QString(extra[0]).toInt() == 0)
						continue;
				}
			}
		}

		QString result_i = mSQLiteDB.getTableValue(i,"article");
		if (dict_type == "xdxf")
			result_i = convertXdxf2Html(result_i);

		mQueryResult.push_back(QueryResult(dict_type, full_name, key_i, result_i));
	};
}

QString DictManager::convertXdxf2Html(QString article)
{
	article.replace(QString("<!--BR-->"), QString("<BR>"));
	article.replace("<c>", "<FONT>");
	article.replace("<c c=", "<FONT COLOR=");
	article.replace("</c>", "</FONT>");

	article.replace("<k>", QString("<FONT COLOR='%1' SIZE=4><B>").arg(xdxf_color_key));
	article.replace("</k>", "</B></FONT>");

	article.replace("<co>", QString("<FONT COLOR='%1'> ").arg(xdxf_color_co));
	article.replace("</co>", "</FONT>");

	article.replace("<ex>", QString("<FONT COLOR='%1'>").arg(xdxf_color_ex));
	article.replace("</ex>", "</FONT>");

	article.replace("<kref>", QString("<FONT COLOR='%1'><U> ").arg(xdxf_color_kref));
	article.replace("</kref>", "</U></FONT>");

	article.replace("<dtrn>", QString("<FONT COLOR='%1'>").arg(xdxf_color_dtrn));
	article.replace("</dtrn>", "</FONT>");

	article.replace("<tense>", QString("<FONT COLOR='%1'><U>").arg(xdxf_color_tense));
	article.replace("</tense>", "</U></FONT>");

	return article;
}

void DictManager::lookup(QString key, QStringList& queryDictList)
{
	mExplanations.clear();
	map<QString, map<QString, QString> >::iterator dict_it;
	for (int i=0; i<queryDictList.size(); i++)
	{
		QString dict = queryDictList[i];
		setQueryResult(dict, key, true);
		if (mQueryResult.empty())
		{
			//LOG	<<"Looking up dictionary "<<dict.toUtf8().data()<<" for word "
			//		<<key.toUtf8().data()<<" and return empty result."<<endl;
			continue;
		}
		else //set Word::mExplanations
		{
			for (int i=0; i<mQueryResult.size(); i++)
			{
				map<QString, QString>& articles = mExplanations[mQueryResult[i].mDictName];
				articles.insert(make_pair(mQueryResult[i].mKey, mQueryResult[i].getFinalArticle()));
			}
		}
	}
}

QStringList DictManager::lookupThesaurus(QString key)
{
	QString dictFile = DICT_OPT.thesaurusDictFileName;
	if (!mSQLiteDB.hasDB(dictFile))
	{
		LOG<<"No database for dictionary \""<<DICT_OPT.thesaurusDictFileName<<"\""<<endl;
		return QStringList();
	}

	QStringList result;
	key.replace("\'", "\'\'");
	QString command = QString("Select key,thesaurus from thesaurus where key >= '%1' and ").arg(key); //a faster query command comparing with 'like' syntax
	QChar last = key[key.size()-1];
	char last_ascii=last.toAscii();
	last_ascii++;
	key.replace(key.size()-1,1,last_ascii);
	command.append(QString("key < '%1';").arg(key));
	mSQLiteDB.execQuery(command, QueryT_Read, dictFile);
	//LOG<<"Command="<<command.toUtf8().data()<<endl;
	for (int i=0; i<mSQLiteDB.getRowCount(); i++)
	{
		QString key_i = mSQLiteDB.getTableValue(i,"key");
		if (key_i.size() > key.size())
		{
			if (key_i[key.size()] != ' ' && key_i[key.size()] != '(' && key_i[key.size()] != '$')
				continue;  //do not display
			else if (key_i.size() > key.size()+1 && key_i[key.size()] == ' ')
			{
				QString extra = key_i.right(key_i.size() - key.size());
				extra = extra.simplified();
				if (!extra.isEmpty() && QString(extra[0]).toInt() == 0)
					continue;
			}
		}

		result<<mSQLiteDB.getTableValue(i,"thesaurus");
	}

	return result;
}

QStringList DictManager::getAvailableDictList()
{
	QStringList list;
	for (int i=0; i<mDicts.size(); i++)
		list<<mDicts[i].mDictName;

	return list;
}

map<QString, QString>* DictManager::getExplanationMap(QString dictName)
{
	map<QString, map<QString, QString> >::iterator dict_it = mExplanations.find(dictName);
	if (dict_it == mExplanations.end())
		return NULL;
	else
		return &(dict_it->second);
}

QStringList DictManager::findKeysBeginWith(QString key, int wordCountMax)
{
	QStringList finalWordList;
	for (	map<int, set<QString> >::iterator key_it = mKeyMap.begin();
			key_it != mKeyMap.end();
			key_it++)
	{
		int len = key_it->first;
		if (len >= key.size())
		{
			for (	set<QString>::iterator set_it = key_it->second.begin();
					set_it != key_it->second.end();
					set_it++)
			{
				QString target = *set_it;
				//if (target.indexOf(key) == 0)
				if (target >= key )
				{
					finalWordList.push_back(target);
					if (finalWordList.size() >= wordCountMax)
						return finalWordList;
				}
			}//end for each set
		}//end if len >= key.size
	}//end for each word group

	return finalWordList;
}

QStringList DictManager::findKeysFuzzyWith(QString key, int wordCountMax, int distanceMax)
{
	int limit = 100;
	QStringList finalWordList;
	map<int, vector<QString> > candidateMap;
	for (	map<int, set<QString> >::iterator key_it = mKeyMap.begin();
			key_it != mKeyMap.end();
			key_it++)
	{
		int len = key_it->first;
		if (len - key.size() <= distanceMax && key.size() - len <= distanceMax)
		{
			for (	set<QString>::iterator set_it = key_it->second.begin();
					set_it != key_it->second.end();
					set_it++)
			{
				QString target = *set_it;
				int result = mEditDistance.CalEditDistance(key.toUtf8().data(), target.toUtf8().data(), limit);
				if (result <= distanceMax)
					candidateMap[result].push_back(target);
			}//end for each set
		}//end if len >= key.size
	}//end for each word group

	for (	map<int, vector<QString> >::iterator can_it = candidateMap.begin();
			can_it != candidateMap.end();
			can_it++)
	{
		//LOG<<"Distance="<<can_it->first<<endl;
		for (int i=0; i<can_it->second.size(); i++)
		{
			finalWordList.push_back(can_it->second[i]);
			//LOG<<"can_it->second[i]="<<can_it->second[i].toUtf8().data()<<endl;
			if (finalWordList.size() >= wordCountMax)
				return finalWordList;
		}
	}

	return finalWordList;
}
